---
title: key_node 夹取型假数字与崩溃修复（静默失败⑰）
tags: [静默失败, 未来函数, 假数字, 检测器校准, A10, A14]
created: 2026-09-12
status: final
---

# key_node 夹取型假数字与崩溃修复

承接 [[key_node未来函数修复_20260912]]（同日，同一文件族）。

## 一、修「KeyError: nan」——崩溃悄悄丢掉了半个报告

**病灶** `key_node_analysis.py:282`
```python
best_bp = bps.loc[bps['ret_20d_after'].idxmax()]
```
`idxmax()` 在**整列全 NaN** 时返回 `nan` → `loc[nan]` 必然 `KeyError`。

**关键认识：全 NaN 是合法输入** —— 该股的节点全落在最近 20 天内，未来收益尚未产生。

| | 修复前 | 修复后 |
|---|---|---|
| 退出码 | rc=1（崩在第四段之后） | **rc=0** |
| 输出行数 | 913 | **1833（翻倍）** |
| 段落 | 4/5（五、总结 永不打印） | **5/5** |
| NaN 守卫触发 | — | **2 次**（真走到，非死代码） |

⇒ 崩塌前打印的 4 段看起来完全正常 → **读者无法分辨报告是残缺的**。这正是本日主题。
⇒ 已加**完成标记**（`✅ 分析完成 | 段落 5/5 | 节点数`），让截断可见。

## 二、新失败类 ⑰ 夹取型假数字（比 NaN 更危险）

**病灶** `key_node_detector.py`（三类节点共 6 个字段）
```python
if i < 25 or i + 5 >= n: return None          # 只守了 5 日
ret_20d = (closes[min(n-1, i+20)] / closes[i] - 1) * 100
```
`i` 在最后 20 天时被夹取到 `n-1` → 算出的「20日收益」**实际是 10 日/3 日收益**，
字段名却写着 `ret_20d`，值是看起来正常的数字。

**实证**
```
_fwd(i=0,k=5) 样本不足 → None        修复后：诚实
旧 min() 夹取          → +30.0%      修复前：标着"5日"实际"3日"的假数字
报告实测: 278 行「后20日—」          修复前这 278 个是错窗口数字
```
**判据**：未来数据不足 → `None`（显式），禁止夹取；下游打印显示 `—`，不得显示 `0.0%`。
**同文件证据**：type2 早已用正确写法（`if i+5 < n else None`）→ 作者知道正确写法，只是漏了另三类。

**类级扫描**：全库仅 2 处真 + 1 处测试文件（`strategies/test_runner.py:44`），已修主体。
**附带**：`vol_after = ... else 0` 是**死变量**（零消费方）+ 假值 → 直接删。

## 三、删掉两处「假声明」（声明与事实漂移）

| 位置 | 原声明 | 实测 |
|---|---|---|
| `key_node_analysis.py:294` | 缩量整理后放量突破 ★ **(最高胜率信号)** | 有效性 **-5.6pp / -7.7pp**（比基准率还差） |
| `key_node_detector.py:5` | 同上 | 同上（**两套并行实现同款假声明**） |
| `key_node_analysis.py` docstring | 入场条件含 **「5日内反弹 > 5%」** | **那正是已删的未来函数** —— 文档不改，下一个人会照着重写这个 bug |

## 四、检测器判据校准链（A10: 17 → 11 → 4 → 0）

```
17 命中
 ├─ CLI 参数类 6 处 (args[i+1] / sys.argv[idx+2])  → 判据排除【假阳性】
 └─ 11 处 → 逐个读代码 → 全部假阳性（遍历已知列表 / 形态统计）
      → 收紧判据为「拒绝型条件」（成立即 continue/break/return 才算挑样本）
      → 4 处（缠论线段划分 bi[j+1]）→ 读代码确认遍历已确定笔序列
      → 记入 ACCEPTED 复核名单（带理由）→ 0
```

**验收硬门（防"靠降灵敏度换 0"）**：用真 bug 的原始形状做反例自证
```
bad_module.py  (模块级 continue 型)  → 抓到 ✅   ← key_node_analysis 修复前的形状
bad_func.py    (函数内 return None型) → 抓到 ✅
good_traverse.py (缠论遍历形状)       → 仍误报 ✗  ← 已知残余，靠复核名单处理
```

**原则**：**能登记就登记，不要靠无限收紧判据消灭假阳性** —— 收到"谁都不报"会引入假阴性，
代价远高于留 4 条带理由的名单。

## 五、结果

```
断言台账 14/14 全绿 · 漂移 0
A10 无未来函数 0/0 ✅（命中 0 + 自校验通过）
A14 检测器台账 0/0 ✅（17 登记 / 17 selftest / FP 实测 4）
```


---
**相关概念**: [[洗盘与出货]] | [[成交量与主力控盘]] | [[买卖点判断]]
